/**
 * WTF Engine
 *
 * License... etc.
 **
 * Win32 Input
 **
 * Author: Sean Micklethwaite
 **/

#include "config.h"

#ifdef _WIN32

#include "input.h"
#include <windowsx.h>
#include "graphics.h"

#define SCANCODE_MASK 0x00FF0000
#define SCANCODE_SHIFT 16

using namespace WtfEngine;


InputHandler::InputHandler()
: Task<InputHandler>(-1000, "Win32 Input Handler"),
  mKeyboardLayout(GetKeyboardLayout(0)),
  mHWnd((HWND)WtfGraphics::Screen::GetInstance().getWindowHandle()), mMousePos(iVector2D(0,0))
{
};

void InputHandler::Run()
{
	MSG msg;
	WCHAR unichar;

	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_INPUT))
	{
		switch (msg.message) {
		case WM_KEYDOWN:
			// Set key state to pressed
			mzKeyState[msg.wParam] = ~0;

			// If we have a printable unicode character, fire a key event. Otherwise,
			// check for modifier keys being pressed, and modify the key flags
			if(ToUnicodeEx(msg.wParam, (msg.lParam & SCANCODE_MASK) >> SCANCODE_SHIFT,
				mzKeyState, &unichar, 1, 0, mKeyboardLayout) == 1)
			{		
				Kernel::GetInstance().ProcessEvent(
					new KeyEvent(InputKey(unichar)));
				
				Kernel::GetInstance().ProcessEvent(
					new KeyDownEvent(InputKey(unichar)));
			};
			
			break;
			
		case WM_KEYUP:
			mzKeyState[msg.wParam] = 0;

			if(ToUnicodeEx(msg.wParam, (msg.lParam & SCANCODE_MASK) >> SCANCODE_SHIFT,
				mzKeyState, &unichar, 1, 0, mKeyboardLayout) == 1)
			{		
				Kernel::GetInstance().ProcessEvent(
					new KeyUpEvent(InputKey(unichar)));
			};
			
			break;



		/**** Mouse Events ****/

		case WM_MOUSEMOVE: {
			static bool bFirst = true;
			int x = GET_X_LPARAM(msg.lParam), y = GET_Y_LPARAM(msg.lParam);

			if(!bFirst) {
				Kernel::GetInstance().ProcessEvent(
					new MouseMoveEvent(
						x, y, x - mMousePos.x(), y - mMousePos.y()));
				bFirst = false;
			};
			
			mMousePos = iVector2D(x, y);
			break;
		}
		case WM_LBUTTONDOWN:
		case WM_RBUTTONDOWN:
		case WM_MBUTTONDOWN: {
			int x = GET_X_LPARAM(msg.lParam), y = GET_Y_LPARAM(msg.lParam);
			Kernel::GetInstance().ProcessEvent(
				new MouseDownEvent(x, y,
					(msg.message == WM_LBUTTONDOWN ? MOUSE_LEFT :
					(msg.message == WM_RBUTTONDOWN ? MOUSE_RIGHT : MOUSE_MIDDLE))
				));
			break;
		}
		case WM_LBUTTONUP:
		case WM_RBUTTONUP:
		case WM_MBUTTONUP: {
			int x = GET_X_LPARAM(msg.lParam), y = GET_Y_LPARAM(msg.lParam);
			Kernel::GetInstance().ProcessEvent(
				new MouseUpEvent(x, y,
					(msg.message == WM_LBUTTONUP ? MOUSE_LEFT :
					(msg.message == WM_RBUTTONUP ? MOUSE_RIGHT : MOUSE_MIDDLE))
				));
			break;
		}
		case WM_MOUSEWHEEL:
		case WM_MOUSEHWHEEL: {
			int x = GET_X_LPARAM(msg.lParam), y = GET_Y_LPARAM(msg.lParam);
			tMouseButton btn = (msg.message == WM_MOUSEWHEEL ?
				(GET_WHEEL_DELTA_WPARAM(msg.wParam) > 0 ? MOUSE_SCROLLUP : MOUSE_SCROLLDOWN) :
				(GET_WHEEL_DELTA_WPARAM(msg.wParam) > 0 ? MOUSE_SCROLLRIGHT : MOUSE_SCROLLLEFT)
			);
			Kernel::GetInstance().ProcessEvent(new MouseDownEvent(x, y, btn));
			Kernel::GetInstance().ProcessEvent(new MouseUpEvent(x, y, btn));

			break;
		}
		case WM_QUIT:
			Kernel::GetInstance().SetSystemStatus(Kernel::SYSTEM_EXITING);
			break;

		default:
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
};

void InputHandler::CaptureAllInput(bool b) {
	if(b) {
		SetCapture(mHWnd);
		ShowCursor(false);
	} else {
		ReleaseCapture();
		ShowCursor(true);
	};
};

void InputHandler::EnterGameMode() {
	
};

void InputHandler::EnterGuiMode() {
	CaptureAllInput(false);
};

iVector2D InputHandler::GetMousePosition() const {
	return mMousePos;
};

bool InputHandler::IsKeyPressed(const WtfEngine::InputKey &key) const {
	return mzKeyState[LOBYTE(VkKeyScanExW(key.unicode, mKeyboardLayout))] > 0;
};

#endif
